#ifndef __GLF_CORE_SOFTWAREMUTEX_H_INCLUDED__
#define __GLF_CORE_SOFTWAREMUTEX_H_INCLUDED__

#include <glf/core/inlines.h>
#include <glf/core/atomic.h>
#include <glf/core/syncEvent.h>
#include <glf/core/lockGuard.h>

namespace glf {

//! Provide software mutual exclusion for thread synchronization.
/** Software mutex use atomic operations to keep track of the locking state, and it will delay
 *  as much as possible kernel calls for the actual locking. This implementation may be faster
 *  than native lock on platform where kernel calls are slow, or can be used for timed_lock
 *  on platform where it isn't available natively.
 *
 *  This implementation is based on boost.
 */
class SoftwareMutex : private NonCopyable
{
public:
	//! Initializes the mutex object.
	SoftwareMutex()
		: mActiveCount(0) {
	}

	//! Releases all resources used by the mutex object.
	~SoftwareMutex() {
	}

	//! Waits for ownership of the specified critical section object. 
	/** The function returns when the calling thread is granted ownership. */
	inline void Lock(unsigned long duration = 0) {
		LockImpl(duration ? duration : (unsigned long)-1);
	}

	//! Attempts to enter a critical section without blocking. 
	/** If the call is successful, the calling thread takes ownership of the 
	 *  critical section.
	 *  
	 *  \return If the critical section is successfully entered, the return value is true.
	 */
	inline bool TryLock() {
		return !AtomicTestAndSetBit(mActiveCount, LOCK_FLAG_BIT);
	}

	//! Releases ownership of the specified critical section object.
	inline void Unlock() {
		// Unset the lock bit. Don't let the fetch_and_add fools you; what we are doing
		// here is a fetch_and_unset_bit. However, we since it's possible that 
		// fetch_and_unset_bit is done internally with a CAS, it's preferable to use
		// fetch_and_add. This is only valid if LOCK_FLAG_BIT == 31
		const unsigned long oldCount = AtomicFetchAndAdd(mActiveCount, LOCK_FLAG_VALUE);

		// If the event flag isn't set and there are locks currently waiting, set the event
		if(!(oldCount & EVENT_FLAG_VALUE) && (oldCount & ~LOCK_FLAG_VALUE))
		{
			if(!AtomicTestAndSetBit(mActiveCount, EVENT_FLAG_BIT))
			{
				mEvent.Signal();
			}
		}
	}

private:
	static const unsigned long LOCK_FLAG_BIT	= 31u; // Cannot be changed (see unlock).
	static const unsigned long EVENT_FLAG_BIT	= 30u;

	static const unsigned long LOCK_FLAG_VALUE	= 1u << LOCK_FLAG_BIT;
	static const unsigned long EVENT_FLAG_VALUE = 1u << EVENT_FLAG_BIT;

	unsigned long  mActiveCount;
	SyncEvent mEvent;

	//! Waits for ownership of the specified critical section object. 
	/** The function returns when the calling thread is granted ownership, or when the 
	 *  time-out interval elapses.
	 *
	 *  \return If the critical section is successfully entered, the return value is true.
	 */
	inline bool LockImpl(unsigned long duration) {
		// Try to aquire the lock !
		static glf::ThreadId thread_id = 0;
		if(!AtomicTestAndSetBit(mActiveCount, LOCK_FLAG_BIT))
		{
			thread_id = glf::GetCurrentThreadId();
			return true;
		}

		if (thread_id == glf::GetCurrentThreadId())
		{
			int x = 0;
			x = x;
		}

		// The lock is already acquired, try to reacquire the lock, or increase the "waiting for lock" count.
		unsigned long oldCount = mActiveCount;
		for(;;)
		{
			// If the LOCK_FLAG is set, then the lock is already in use and we increase the "waiting for lock" 
			// count, otherwise, we set the LOCK_FLAG in an attempt to reacquire the lock.
			const unsigned long newCount = (oldCount & LOCK_FLAG_VALUE) ? (oldCount + 1) : (oldCount | LOCK_FLAG_VALUE);
			const unsigned long currentCount = AtomicCompareAndSwap(mActiveCount, newCount, oldCount);

			// Did our swap succeed ? However, at this point we do not know if we acquired the lock or not.
			if(currentCount == oldCount)
			{
				break;
			}

			oldCount = currentCount;
		}

		// Did we _not_ aquire the lock ? (If the lock was acquired, it implies oldCount didn't have the lock flag.)
		if(oldCount & LOCK_FLAG_VALUE)
		{
			bool lockAcquired = false;

			do
			{
				// Since we don't have the lock, wait...
				//
				// When the event is signal, the EVENT_FLAG is set: this is to prevent to send multiple signal
				// (and thus, wake multiple threads) while a woke-up thread is currently doing computation in order
				// to aquire the lock. When the woke-up thread has finish requaring the lock, or returning to
				// sleep if someone else aquired the lock, that thread is responsible to unset the EVENT_FLAG.
				if(!mEvent.Wait(duration))
				{
					AtomicDecrement(mActiveCount);
					return false;
				}

				// The lock was released, try to aquire it !
				//
				// In order for the code to reach this point, some manipulations of the mActiveCount has been done by
				// unlock. We will manually redo those manipulations. While it may seems strange to do those manipulations 
				// by hand on an old variable instead of writing oldCount = mActiveCount, this method is more reliable 
				// and faster: Since mActiveCount isn't volatile, the compiler don't guaranty that the value of 
				// mActiveCount will be re-read from memory. Also, our assumption will be valid if we are not in a heavy 
				// contengency.
				oldCount &= ~LOCK_FLAG_VALUE;	// Remove the lock flag.
				oldCount |= EVENT_FLAG_VALUE;	// Set the event flag bit.
				++oldCount;					    // Increment the "waiting for lock" count.

				for(;;)
				{
					// The algorithm here differ sligly from the one presented above. This is because we already
					// increase the usage count, and that we have to unset the EVENT_FLAG.
					const unsigned long newCount = ((oldCount & LOCK_FLAG_VALUE)
											         ? (oldCount)
											         : ((oldCount - 1) | LOCK_FLAG_VALUE)) 
											       & ~EVENT_FLAG_VALUE;
					const unsigned long currentCount = AtomicCompareAndSwap(mActiveCount, newCount, oldCount);

					// Did our swap succeed ? Again, at this point we do not know if we acquired the lock or not.
					if(currentCount == oldCount)
					{
						break;
					}

					oldCount = currentCount;
				}

				// Did we aquire the lock ? If not, return to sleep.
				lockAcquired = !(oldCount & LOCK_FLAG_VALUE);
			}
			while(!lockAcquired);
		}

		thread_id = glf::GetCurrentThreadId();
		// We aquired the lock !
		return true;
	}

};

typedef LockGuard<SoftwareMutex>    SoftwareLockScope;
typedef UnlockGuard<SoftwareMutex>  SoftwareUnlockScope;
typedef TryLockGuard<SoftwareMutex> SoftwareTryLockScope;


} // namespace glf

#endif // __GLF_CORE_SOFTWAREMUTEX_H_INCLUDED__
